创建父工程 创建父工程 edu-parent,删除 src 目录
添加 packaging 配置为 pom,并将 spring-boot-starter
改为 spring-boot-starter-web
添加依赖管理(管理公共依赖的 jar 包的版本)
创建子模块 选择 sprint 的方式或者 maven 额创建都可以,但是推荐使用 maven 的方式,因为这样可以选择关联父工程。
创建启动类
1 2 3 4 5 6 7 8 9 10 11 package com.yanrs.me;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class EduTeacherApplication { public static void main (String[] args) { SpringApplication.run(EduTeacherApplication.class , args ) ; } }
添加配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # 服务端口 server.port=8001 # 服务名 spring.application.name=ude-teacher # 环境设置:dev、test、prod spring.profiles.active=dev # mysql数据库连接 spring.datasource.driver-class -name =com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql: spring.datasource.username=root spring.datasource.password=123456 #mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
添加依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <dependencies> <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> </dependency> <!--swagger--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> </dependency> <!--lombok用来简化实体类:需要安装lombok插件--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--开发者工具--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> </dependency> </dependencies>
代码生成器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 package com.yanrs.edu.teacher;import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.generator.AutoGenerator;import com.baomidou.mybatisplus.generator.config.DataSourceConfig;import com.baomidou.mybatisplus.generator.config.GlobalConfig;import com.baomidou.mybatisplus.generator.config.PackageConfig;import com.baomidou.mybatisplus.generator.config.StrategyConfig;import com.baomidou.mybatisplus.generator.config.rules.DateType;import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;import org.junit.Test;public class CodeGenerator { @Test public void run () { AutoGenerator mbg = new AutoGenerator(); GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir" ); gc.setOutputDir(projectPath + "/src/main/java" ); gc.setAuthor("rex" ); gc.setOpen(false ); gc.setFileOverride(false ); gc.setServiceName("%sService" ); gc.setIdType(IdType.ID_WORKER); gc.setDateType(DateType.ONLY_DATE); gc.setSwagger2(true ); mbg.setGlobalConfig(gc); DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://127.0.0.1:3306/edu" ); dsc.setDriverName("com.mysql.jdbc.Driver" ); dsc.setUsername("root" ); dsc.setPassword("123456" ); dsc.setDbType(DbType.MYSQL); mbg.setDataSource(dsc); PackageConfig pc = new PackageConfig(); pc.setModuleName("teacher" ); pc.setParent("com.yanrs.edu" ); pc.setController("controller" ); pc.setEntity("entity" ); pc.setService("service" ); pc.setMapper("mapper" ); mbg.setPackageInfo(pc); StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("edu_teacher" ); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setTablePrefix(pc.getModuleName() + "_" ); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true ); strategy.setRestControllerStyle(true ); strategy.setControllerMappingHyphenStyle(true ); mbg.setStrategy(strategy); mbg.execute(); } }
运行后就能在对应的包下生成对应的代码
接口开发-获取所有讲师 开发一个接口,测试项目是否能运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.yanrs.edu.teacher.controller;import com.yanrs.edu.teacher.entity.Teacher;import com.yanrs.edu.teacher.service.TeacherService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController @RequestMapping ("/edu/teacher" )public class TeacherController { @Autowired private TeacherService teacherService; @GetMapping public List<Teacher> getAllTeacher () { return teacherService.list(null ); } }
默认的返回中,时间的显示都是带有时区的,可以在配置文件中进行配置,让其返回正常的时间格式。
1 2 3 # 时间显示 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8
接口开发 - 讲师删除 实体类中添加为逻辑删除字段添加 TableLogic 注解
1 2 3 @TableLogic @ApiModelProperty (value = "逻辑删除 1(true)已删除, 0(false)未删除" )private Boolean isDeleted;
配置类中添加逻辑删除插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.yanrs.edu.teacher.config;import com.baomidou.mybatisplus.core.injector.ISqlInjector;import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.transaction.annotation.EnableTransactionManagement;@Configuration @EnableTransactionManagement @MapperScan ("com.yanrs.edu.teacher.mapper" )public class EduTeacherConfig { @Bean public ISqlInjector iSqlInjector () { return new LogicSqlInjector(); } }
controller 中添加删除方法
1 2 3 4 5 @DeleteMapping ("{id}" )public Boolean deleteTeacherById (@PathVariable("id" ) String id) { return teacherService.removeById(id); }
配置 Swagger2 确认引入了 swagger 的依赖,新建 Swagger 配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.yanrs.edu.teacher.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.service.ApiInfo;import springfox.documentation.service.Contact;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;import springfox.documentation.swagger2.annotations.EnableSwagger2;@Configuration @EnableSwagger 2public class SwaggerConfig { @Bean public Docket webApiConfig () { return new Docket(DocumentationType.SWAGGER_2) .groupName("webApi" ) .apiInfo(webApiInfo()) .select() .build(); } private ApiInfo webApiInfo () { return new ApiInfoBuilder() .title("网站-讲师管理API文档" ) .description("本文档描述了讲师管理微服务接口定义" ) .version("1.0" ) .contact(new Contact("jack" , "http://atguigu.com" , "1111@qq.com" )) .build(); } }
创建公共模块 封装统一返回, 创建统一返回枚举类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.yanrs.edu.common;public enum ResponseCode { SUCCESS(90000 , "成功" ), FAIL(30010 , "成功" ), ERROR(30011 , "错误" ), NOT_FOUND(30012 , "资源未找到" ), NOT_AUTHED(30013 , "无权限,访问拒绝" ), PARAM_INVAILD(30014 , "提交参数非法" ); private Integer code; private String message; public Integer getCode () { return code; } public String getMessage () { return message; } public void setCode (Integer code) { this .code = code; } public void setMessage (String message) { this .message = message; } ResponseCode(Integer code, String message) { this .code = code; this .message = message; } }
创建统一返回类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package com.yanrs.edu.common;import lombok.Data;import java.util.HashMap;import java.util.Map;@Data public class R { private Boolean success; private Integer code; private String message; private Map<String, Object> data = new HashMap<>(); public static R success () { R r = new R(); r.setSuccess(true ); r.setCode(ResponseCode.SUCCESS.getCode()); r.setMessage("操作成功" ); return r; } public static R fail () { R r = new R(); r.setSuccess(false ); r.setCode(ResponseCode.FAIL.getCode()); r.setMessage("操作失败" ); return r; } public R success (Boolean success) { this .setSuccess(success); return this ; } public R message (String message) { this .setMessage(message); return this ; } public R code (Integer code) { this .setCode(code); return this ; } public R data (String key, Object value) { this .data.put(key, value); return this ; } public R data (Map<String, Object> map) { this .setData(map); return this ; } }
在其他子工程中,引入 common 的依赖
1 2 3 4 5 6 <!--common 依赖 --> <dependency> <groupId>com.yanrs.me</groupId> <artifactId>edu-common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
接口开发-讲师分页查询 controller
1 2 3 4 5 6 7 @GetMapping ("/list/{currentPage}/{pageSize}" )public R getPageTeacher (@PathVariable("currentPage" ) Integer currentPage, @PathVariable ("pageSize" ) Integer pageSize) { Page<Teacher> teacherPage = new Page<>(currentPage, pageSize); teacherService.page(teacherPage, null ); return R.success().data("total" , teacherPage.getTotal()).data("items" , teacherPage.getRecords()); }
还要在配置类中配置分页插件,不然 total 值为空
1 2 3 4 5 6 7 8 @Bean public PaginationInterceptor paginationInterceptor () { return new PaginationInterceptor(); }
接口开发-讲师条件查询且分页 controller
1 2 3 4 5 6 7 @GetMapping ("/condition/{currentPage}/{pageSize}" )public R getConditionPageTeacher (@PathVariable("currentPage" ) Integer currentPage, @PathVariable ("pageSize" ) Integer pageSize, TeacherListReqVo teacherListReqVo) { Page<Teacher> teacherPage = new Page<>(currentPage, pageSize); teacherService.getTeacherListByCondition(teacherPage, teacherListReqVo); return R.success().data("total" , teacherPage.getTotal()).data("items" , teacherPage.getRecords()); }
接口开发 - 讲师新增 新建 AddTeacherReqVo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.yanrs.edu.teacher.entity.vo;import lombok.Data;@Data public class AddTeacherReqVo { private String name; private String intro; private Integer level; private String career; private String avatar; private Integer sort; private Boolean isDeleted; }
Controller 中
1 2 3 4 5 6 7 8 9 10 11 12 @PostMapping public R addTeacher (@RequestBody AddTeacherReqVo addTeacherReqVo) { Teacher teacher = new Teacher(); BeanUtils.copyProperties(addTeacherReqVo, teacher); boolean save = teacherService.save(teacher); if (save) { return R.success().data("teacher" , teacher); } else { return R.fail(); } }
还需在实体类上设置 gmtCreate 和 gmtModified 字段的自动填充
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package com.yanrs.edu.teacher.entity;import com.baomidou.mybatisplus.annotation.*;import java.util.Date;import java.io.Serializable;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;import lombok.EqualsAndHashCode;import lombok.experimental.Accessors;@Data @EqualsAndHashCode (callSuper = false )@Accessors (chain = true )@TableName ("edu_teacher" )@ApiModel (value="Teacher对象" , description="讲师" )public class Teacher implements Serializable { private static final long serialVersionUID = 1L ; @ApiModelProperty (value = "讲师ID" ) @TableId (value = "id" , type = IdType.ID_WORKER_STR) private String id; @ApiModelProperty (value = "讲师姓名" ) private String name; @ApiModelProperty (value = "讲师资历,一句话说明讲师" ) private String intro; @ApiModelProperty (value = "讲师简介" ) private String career; @ApiModelProperty (value = "头衔 1高级讲师 2首席讲师" ) private Integer level; @ApiModelProperty (value = "讲师头像" ) private String avatar; @ApiModelProperty (value = "排序" ) private Integer sort; @TableLogic @ApiModelProperty (value = "逻辑删除 1(true)已删除, 0(false)未删除" ) private Boolean isDeleted; @ApiModelProperty (value = "创建时间" ) @TableField (fill = FieldFill.INSERT) private Date gmtCreate; @ApiModelProperty (value = "更新时间" ) @TableField (fill = FieldFill.INSERT_UPDATE) private Date gmtModified; }
新建 MybatisPlusMetaHandler 类继承 MetaObjectHandler 用于处理自动填充。这里特别要注意⚠️字段名称不是数据库中的,而是实体类中的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.yanrs.edu.teacher.handler;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;import java.util.Date;@Component public class MybatisPlusMetaHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { this .setFieldValByName("gmtCreate" , new Date(), metaObject); this .setFieldValByName("gmtModified" , new Date(), metaObject); } @Override public void updateFill (MetaObject metaObject) { this .setFieldValByName("gmtModified" , new Date(), metaObject); } }
接口开发 - 讲师修改 新建 UpdateTeacherReqVo 继承自 AddTeacherReqVo
1 2 3 4 5 6 7 package com.yanrs.edu.teacher.entity.vo;import lombok.Data;@Data public class UpdateTeacherReqVo extends AddTeacherReqVo {}
controller 中
1 2 3 4 5 6 7 8 9 10 11 12 13 @PutMapping ("{id}" ) public R updateTeacherById (@PathVariable("id" ) String id, @RequestBody UpdateTeacherReqVo updateTeacherReqVo) { Teacher teacher = new Teacher(); BeanUtils.copyProperties(updateTeacherReqVo, teacher); teacher.setId(id); boolean update = teacherService.updateById(teacher); if (update) { return R.success().data("teacher" , teacher); } else { return R.fail(); } }
统一异常处理 在 handler 中新建 GlobalExceptionHandler 类,用于进行统一异常处理,当发生异常的时候就能捕捉到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.yanrs.edu.teacher.handler;import com.yanrs.edu.common.R;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler (Exception.class ) @ResponseBody public R error (Exception e ) { e.printStackTrace(); return R.error(); } @ExceptionHandler (ArithmeticException.class ) @ResponseBody public R error (ArithmeticException e ) { e.printStackTrace(); return R.error().message("除数不能为 0" ); } }
自定义异常 新建 EduException 类,继承 RuntimeException,类中有两个属性 code 和 message
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.yanrs.edu.teacher.handler;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class EduException extends RuntimeException { private Integer code; private String message; }
在 GlobalExceptionHandler 对 EduException 进行处理,当发生 EduException 的时候,接口返回相关信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.yanrs.edu.teacher.handler;import com.yanrs.edu.common.R;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler (Exception.class ) @ResponseBody public R error (Exception e ) { e.printStackTrace(); return R.error(); } @ExceptionHandler (ArithmeticException.class ) @ResponseBody public R error (ArithmeticException e ) { e.printStackTrace(); return R.error().message("除数不能为 0" ); } @ExceptionHandler (EduException.class ) @ResponseBody public R error (EduException e ) { e.printStackTrace(); return R.error().message(e.getMessage()).code(e.getCode()); } }
在需要使用的时候加上自定义异常即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @PutMapping ("{id}" )public R updateTeacherById (@PathVariable("id" ) String id, @RequestBody UpdateTeacherReqVo updateTeacherReqVo) { if (updateTeacherReqVo==null ){ throw new EduException(ResponseCode.PARAM_INVAILD.getCode(), ResponseCode.PARAM_INVAILD.getMessage()); } Teacher teacher = new Teacher(); BeanUtils.copyProperties(updateTeacherReqVo, teacher); teacher.setId(id); boolean update = teacherService.updateById(teacher); if (update) { return R.success().data("teacher" , teacher); } else { return R.fail(); } }
日志 头像上传 OSS controller
1 2 3 4 5 6 7 8 9 10 11 @PostMapping ("/upload/avatar" ) public R uploadAvatar (@RequestParam("file" ) MultipartFile file) { try { String url = aliOss.UploadFile(file.getOriginalFilename(), file.getInputStream()); return R.success().data("url" , url); } catch (Exception e) { e.printStackTrace(); return R.fail(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.yanrs.edu.teacher.components;import com.aliyun.oss.OSS;import com.aliyun.oss.OSSClientBuilder;import org.joda.time.DateTime;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import java.io.InputStream;import java.util.UUID;@Component public class AliOss { @Value ("${aliyun.oss.file.endpoint}" ) private String endpoint; @Value ("${aliyun.oss.file.keyid}" ) private String keyId; @Value ("${aliyun.oss.file.keysecret}" ) private String keySecret; @Value ("${aliyun.oss.file.bucketname}" ) private String bucketName; @Value ("${aliyun.oss.file.foldername}" ) private String folderName; public String UploadFile (String fileName, InputStream fileInputStream) { OSS ossClient = new OSSClientBuilder().build(endpoint, keyId, keySecret); String uploadFileName = new DateTime().toString("yyyy/MM/dd" ) + "/" + UUID.randomUUID().toString().replace("-" , "" ) + "_" + fileName; ossClient.putObject(bucketName, uploadFileName, fileInputStream); ossClient.shutdown(); return "https://" + bucketName + "." + endpoint + "/" + uploadFileName; } }
POI Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。
1 2 3 4 5 HSSF - 提供读写Microsoft Excel格式档案的功能。(.xls) XSSF - 提供读写Microsoft Excel OOXML格式档案的功能。(.xlsx) HWPF - 提供读写Microsoft Word格式档案的功能。 HSLF - 提供读写Microsoft PowerPoint格式档案的功能。 HDGF - 提供读写Microsoft Visio格式档案的功能。
创建一个普通的 maven 工程,并引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <dependency > <groupId > org.apache.poi</groupId > <artifactId > poi</artifactId > <version > 3.9</version > </dependency > <dependency > <groupId > org.apache.poi</groupId > <artifactId > poi-ooxml</artifactId > <version > 3.9</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > </dependency >
写操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.yanrs.me;import org.apache.poi.hssf.usermodel.HSSFWorkbook;import org.apache.poi.ss.usermodel.Cell;import org.apache.poi.ss.usermodel.Row;import org.apache.poi.ss.usermodel.Sheet;import org.apache.poi.ss.usermodel.Workbook;import org.apache.poi.xssf.usermodel.XSSFWorkbook;import org.junit.Test;import java.io.FileOutputStream;public class PoiDemo { @Test public void testWrite () throws Exception { Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("会员列表" ); Row row = sheet.createRow(0 ); Cell cell = row.createCell(0 ); cell.setCellValue("131398612389698163" ); FileOutputStream fileOutputStream = new FileOutputStream("/tmp/xxx.xlsx" ); workbook.write(fileOutputStream); fileOutputStream.close(); } }
读操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Test public void testRead () throws Exception { FileInputStream fileInputStream = new FileInputStream("/tmp/xxx.xlsx" ); XSSFWorkbook workbook = new XSSFWorkbook(fileInputStream); XSSFSheet sheet = workbook.getSheet("会员列表" ); XSSFRow row = sheet.getRow(0 ); XSSFCell cell = row.getCell(0 ); System.out.println(cell.getStringCellValue()); fileInputStream.close(); }
Excel 03 版本和 07 版本的区别在于,03 版本只能写入最多 65535 条数据,而 07 版本则没有限制。如果我们要写入的数据量很大,超过100万条甚至更多条,那么我们可以使用 SXSSF
代码地址
创建视频模块 因为阿里云视频 “Java上传SDK” 并没有在 Maven 仓库中,所以需要将下载的 aliyun-java-vod-upload-1.4.12.jar 安装到本地 maven 仓库中。切换到下载 jar 包目录,执行以下命令
1 mvn install:install-file -DgroupId=com.aliyun -DartifactId=aliyun-sdk-vod-upload -Dversion=1.4.12 -Dpackaging=jar -Dfile=aliyun-java-vod-upload-1.4.12.jar
引入刚刚安装的依赖
1 2 3 4 5 6 <dependency > <groupId > com.aliyun</groupId > <artifactId > aliyun-sdk-vod-upload</artifactId > <version > 1.4.12</version > </dependency >
创建 video 模块
创建完成之后如果 idea 识别这不是一个 springboot 工程,那么可以这么操作。将 java 目录修改为 Source Root,resource 目录修改为 Resource Root 。
修改完成之后可以拷贝一个配置文件到 resource 下面,看看配置文件是否发生变化。
如果还不生效,那么可以删除改模块,重新创建一个不同名字的模块。
在 pom 中引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <dependencies > <dependency > <groupId > com.yanrs.me</groupId > <artifactId > edu-common</artifactId > <version > 0.0.1-SNAPSHOT</version > </dependency > <dependency > <groupId > com.aliyun</groupId > <artifactId > aliyun-java-sdk-core</artifactId > <version > 4.3.3</version > </dependency > <dependency > <groupId > com.aliyun.oss</groupId > <artifactId > aliyun-sdk-oss</artifactId > <version > 3.1.0</version > </dependency > <dependency > <groupId > com.aliyun</groupId > <artifactId > aliyun-java-sdk-vod</artifactId > <version > 2.15.0</version > </dependency > <dependency > <groupId > com.aliyun</groupId > <artifactId > aliyun-sdk-vod-upload</artifactId > <version > 1.4.12</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.28</version > </dependency > </dependencies >
项目加载读取配置文件 在项目一启动的时候就加载配置文件中的配置,并将配置的内容赋值给类中的常量,这样在后续使用的时候,直接类名.常量名 直接使用即可。上面的头像上传 OSS 模块不是这么设计的,不是在项目启动的时候就加载了配置文件,而是每次调用的时候才去获取。这两种方式都是获取配置文件的方式,使用哪一种都可以。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.yanrs.edu.videotape.utils;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Value;public class ConstantPropertiesUtil implements InitializingBean { @Value ("${aliyun.vod.file.keyid}" ) private String keyId; @Value ("${aliyun.vod.file.keysecret}" ) private String keySecret; public static String ACCESS_KEY_ID; public static String ACCESS_KEY_SECRET; @Override public void afterPropertiesSet () { ACCESS_KEY_ID = keyId; ACCESS_KEY_SECRET = keySecret; } }
Nginx 反向代理 因为 teacher 模块相关的路由后端使用的是 8001 端口,视频模块后端使用的是 8002 端口。而只能在前端配置一个端口,所以需要借助 nginx,让其对路由进行区分,不同的路由访问不同的端口。目前所有路由信息如下:
nginx 配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 server { listen 9001; server_name localhost; location ~/edu/course/ { proxy_pass http://192.168.1.3:8001; } location ~/edu/subject/ { proxy_pass http://192.168.1.3:8001; } location ~/edu/chapter/ { proxy_pass http://192.168.1.3:8001; } location ~/edu/video/ { proxy_pass http://192.168.1.3:8001; } location ~/edu/teacher/ { proxy_pass http://192.168.1.3:8001; } location ~/edu/videotape/ { proxy_pass http://192.168.1.3:8002; } }
ngxin docker 启动命令如下1 docker run -d -p 9001:9001 -p 8080:8080 --name nginx -v /Users/rex/Documents/server/nginx/www/:/usr/share/nginx/html -v /Users/rex/Documents/server/nginx/conf/nginx.conf:/etc/nginx/nginx.conf docker.io/nginx
这样配置之后,前端只需要访问 9001 端口即可,nginx 会根据不同的 url 地址来访问不同的后端接口。
1 2 3 4 module .exports = merge(prodEnv, { NODE_ENV: '"development"' , BASE_API: '"http://127.0.0.1:9001"' , })
创建注册中心 父工程 pom 文件中指定Spring Cloud 版本
1 2 3 4 5 6 7 8 9 10 11 12 <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-dependencies</artifactId > <version > Finchley.SR2</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement >
创建 edu-register 模块,在 pom 文件中增加 eureka-server 的依赖
1 2 3 4 5 6 7 <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-eureka-server</artifactId > </dependency > </dependencies >
创建启动类,并添加注解
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.yanrs.edu.register;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication @EnableEurekaServer public class EduRegisterApplication { public static void main (String[] args) { SpringApplication.run(EduRegisterApplication.class , args ) ; } }
增加配置文件
1 2 3 4 5 6 7 8 9 #服务端口 server.port=8000 #是否将自己注册到Eureka服务器中,本身是服务器,无需注册 eureka.client.register-with-eureka=false #是否从Eureka中获取注册信息 eureka.client.fetch-registry=false #Eureka客户端与Eureka服务端进行通信的地址 eureka.client.service-url.defaultZone=http://127.0.0.1:${server.port}/eureka/
启动注册中心,测试地址 http://127.0.0.1:8000/ 能否访问成功
注册到注册中心 在 teacher,videotape 的 pom 文件中添加 eureka-client 依赖
1 2 3 4 5 <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-eureka-client</artifactId > </dependency >
在 teacher,videotape 的配置文件中添加配置
1 2 3 4 #指定注册中心地址 eureka.client.service-url.defaultZone=http://127.0.0.1:8000/eureka/ #eureka服务器上获取的是服务器的ip地址,否则是主机名 eureka.instance.prefer-ip-address=true
在 teacher,videotape 的启动类中添加注解
最后,先启动注册中心,在启动 teacher,videotape 服务,访问注册中心页面地址 http://127.0.0.1:8000/ 观察服务是否注册到其中
服务调用 在 teacher 中,调用 videotape 中的服务。在 teacher 中添加 feign 的注解,并在启动类中加上 @EnableFeignClients
注解
1 2 3 4 5 <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-openfeign</artifactId > </dependency >
新建 feign 模块,并在里面新建 VideotapeFeign 接口,接口中有需要被远程调用的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.yanrs.edu.teacher.feign;import com.yanrs.edu.common.R;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.DeleteMapping;import org.springframework.web.bind.annotation.PathVariable;@Component @FeignClient ("edu-videotape" ) public interface VideotapeFeign { @DeleteMapping ("edu/videotape/{id}" ) public R deleteVideoById (@PathVariable("id" ) String id) ; }